/** ***********************************************************************************
* @file         rtc_task.c
* @brief        系统RTC管理任务
* @details      主要用来管理系统RTC时钟，以及数据接口
* @author       杨小珛
* @date         2021/01/05
* @copyright    北京高知科技有限公司    
* @version      V1.0
* @attention    无
**************************************************************************************/

#include "rtc_task.h"

#define RTC_HARDARE_TIMING          ( 1 )

GaozSemaphoreHandle_t   rtc_info_mutex; //RTC数据互斥

static uint8_t rtc_get_io(en_sys_rtc_type_t str_type, rtc_struct *p_time);
static uint8_t rtc_set_io(en_sys_rtc_type_t str_type, rtc_struct *p_time);
static uint32_t rtc_to_count( en_sys_rtc_type_t time_mode, rtc_struct *p_rtc );
static void count_to_rtc( uint32_t TimerCount, en_sys_rtc_type_t time_mode, rtc_struct *p_rtc );

static uint32_t rtc_get( en_sys_rtc_type_t time_mode, void* pv );
static uint32_t rtc_set( en_sys_rtc_type_t time_mode, void* pv );

/** ****************************************************************************
* @remarks       void rtc_create_task( unsigned portBASE_TYPE uxPriority )
* @brief         创建RTC数据处理任务
* @param[in]     uxPriority 任务的优先级
* @param[out]    无
* @return        无
* @attention     无
*******************************************************************************/
void rtc_create_task( unsigned portBASE_TYPE uxPriority )
{
    rtc_info_mutex = gaoz_mutex_create();
    if( rtc_info_mutex == 0 )
    {
        while(1); /*创建失败*/
    }
}

/** ****************************************************************************
* @remarks       void rtc_utc_to_local( rtc_struct* ptm, char zone )
* @brief         GPS时间转换为北京时间
* @param[in]     * ptm RTC时间，年，月，日，时，分,秒
* @param[in]     zone 时区小时偏移
* @param[out]    无
* @return        无
* @attention     GPS时间 + 8 小时 = 北京时间
*******************************************************************************/
void rtc_utc_to_local( rtc_struct* ptm, char zone )
{
    ptm->hour += zone;
    
    // 没有跨天，则计算完成
    if( ptm->hour < 24 )
        return;

    // 跨天后的计算
    ptm->hour -= 24;
    ptm->day  += 1;

    if( ptm->month == 2 )      // 针对2月处理:闰年/非闰年
    {
        if( ptm->year % 4 == 0 )
        {
            if( ptm->day == 30 ) // 闰年处理:ptm->month=3，ptm->day=1
            {
                ptm->day = 1;
                ptm->month = 3;
            }
        }
        else
        {
            if( ptm->day == 29 ) // 非闰年处理:ptm->month=3，ptm->day=1
            {
                ptm->day = 1;
                ptm->month = 3;
            }
        }
    }

    if( ptm->day == 32 )     // 针对1,3,5,7,8,10,12月处理
    {
        if( ptm->month == 12 ) // 12月处理
        {
            ptm->month = 1; // ptm->month=1,ptm->day=1,ptm->year=ptm->year+1
            ptm->day = 1;
            ptm->year += 1;
        }
        else
        {
            switch( ptm->month )  // 1,3,5,7,8,10月处理
            {
                case 1:
                case 3:
                case 5:
                case 7:
                case 8:
                case 10:
                    ptm->month += 1;  // ptm->month=ptm->month+1,ptm->day=1
                    ptm->day = 1;
                    break;
                default:
                    break;
            }
        }
    }

    if( ptm->day == 31 )     // 针对4,6,9,11月处理
    {
        switch( ptm->month )
        {
            case 4:
            case 6:
            case 9:
            case 11:
                ptm->month += 1;  // ptm->month=ptm->month+1,ptm->day=1
                ptm->day = 1;
                break;
            default:
                break;
        }
    }
}
#if 1
/** ****************************************************************************
* @remarks       uint8_t rtc_local_to_utc(struct tm *p_local_time, struct tm *p_utc_time)
* @brief         将本地时间转换为UTC时间
* @param[in]     pLocalTime - 要输入的本地时间
* @param[out]     pUTCTime - 要输出的RTC时间
* @return        无
* @attention     无
*******************************************************************************/
uint8_t rtc_local_to_utc(struct tm *p_local_time, struct tm *p_utc_time)
{
    int nyear, nMon, nday, nhour, nMin, nSec;
    unsigned char nMonday1[12] = {31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
    
    if(p_local_time == NULL || p_utc_time == NULL)
    {
        return 1;
    }
    nyear = p_local_time->tm_year + 1900;
    nMon = p_local_time->tm_mon;
    nday = p_local_time->tm_mday ;
    nhour = p_local_time->tm_hour - 8;
    nMin = p_local_time->tm_min;
    nSec = p_local_time->tm_sec;
    
    if(nhour < 0)
    {
        nhour += 24;
        nday -= 1;
        
        if(nday <= 0)
        {
            if((nyear % 400 == 0)||(nyear % 4 == 0 && nyear % 100 != 0))  //判断是否为闰年，年号能被400整除或年号能被4整除，而不能被100整除为闰年
            {
                nMonday1[1] = 29;
            }//为闰年
            else
            {
                nMonday1[1] = 28;
            }//为平年
            
            nMon -= 1;
            if(nMon >= 0)
            {
                nday = nMonday1[nMon];
            }
            else
            {
                nyear = nyear - 1;
                nMon = 11;
                nday = nMonday1[nMon];
            }
        }
    }
    
    p_utc_time->tm_year = nyear - 1900;
    p_utc_time->tm_mon = nMon;
    p_utc_time->tm_mday = nday;
    p_utc_time->tm_hour = nhour;
    p_utc_time->tm_min = nMin;
    p_utc_time->tm_sec = nSec;
    
    return 0;
}
#endif
/** ****************************************************************************
* @remarks       void rtc_bcd_to_int( RTC_Struct *Time )
* @brief         将BCD码格式的RTC数据转变为int型
* @param[in]     *rtc 输入RTC时间数据地址
* @param[out]    *rtc 输出RTC时间数据地址
* @return        无
* @attention     无
*******************************************************************************/
void rtc_bcd_to_int( rtc_struct *rtc )
{
    uint16_t  temp;
    temp =  rtc->year;
    rtc->year   = (( temp & 0xF000 ) >> 12 )*1000 + (( temp & 0x0F00 ) >> 8 )* 100 + (( temp & 0x00F0 ) >> 4 ) * 10 + ( temp & 0x000F );
    
    temp =  rtc->month;
    rtc->month  = (( temp & 0x00F0 ) >> 4 ) * 10 + ( temp & 0x000F );

    temp =  rtc->day;
    rtc->day  = (( temp & 0x00F0 ) >> 4 ) * 10 + ( temp & 0x000F );

    temp =  rtc->hour;
    rtc->hour  = (( temp & 0x00F0 ) >> 4 ) * 10 + ( temp & 0x000F );

    temp =  rtc->minute;
    rtc->minute  = (( temp & 0x00F0 ) >> 4 ) * 10 + ( temp & 0x000F );

    temp =  rtc->second;
    rtc->second  = (( temp & 0x00F0 ) >> 4 ) * 10 + ( temp & 0x000F );
}

/** ****************************************************************************
* @remarks       void rtc_int_to_bcd( rtc_struct *rtc )
* @brief         将int型RTC数据转变为BCD码格式
* @param[in]     *rtc 输入RTC时间数据地址
* @param[out]    *rtc 输出RTC时间数据地址
* @return        无
* @attention     无
*******************************************************************************/
void rtc_int_to_bcd( rtc_struct *rtc )
{
    uint16_t  temp;
    
    temp =  rtc->year;
    rtc->year   = (( temp / 1000 ) << 12 ) + (( temp / 100 ) << 8 ) + (( temp / 10 ) << 4 ) + ( temp % 10 );
    temp =  rtc->month;
    rtc->month  = (( temp / 10 ) << 4 ) + ( temp % 10 );
    temp =  rtc->day;
    rtc->day  = (( temp / 10 ) << 4 ) + ( temp % 10 );
    temp =  rtc->hour;
    rtc->hour  = (( temp / 10 ) << 4 ) + ( temp % 10 );
    temp =  rtc->minute;
    rtc->minute  = (( temp / 10 ) << 4 ) + ( temp % 10 );
    temp =  rtc->second;
    rtc->second  = (( temp / 10 ) << 4 ) + ( temp % 10 );
}

/** ****************************************************************************
* @remarks       static uint32_t rtc_to_count( en_sys_rtc_type_t time_mode, rtc_struct *p_rtc )
* @brief         将RTC时间转变为时间戳
* @param[in]     time_mode 输入时钟数据的模式
* @param[in]     *p_rtc 输出RTC时间数据地址
* @param[out]    无
* @return        获取RTC数据长度
* @attention     无
*******************************************************************************/
static uint32_t rtc_to_count( en_sys_rtc_type_t time_mode, rtc_struct *p_rtc )
{
    struct tm t_local, t_utc;
    uint32_t nResult;
    rtc_struct rtc;

    memcpy(&rtc, p_rtc, sizeof(rtc_struct));
    
    if(time_mode == RTC_TIM_CAL_BCD)
        rtc_bcd_to_int(&rtc); // TODO : 
    
    memset(&t_local , 0x00 , sizeof(t_local));
    memset(&t_utc , 0x00 , sizeof(t_utc));
    
    t_local.tm_year = rtc.year - 1900;
    t_local.tm_mon = rtc.month - 1;
    t_local.tm_mday = rtc.day;
    t_local.tm_hour = rtc.hour;
    t_local.tm_min = rtc.minute;
    t_local.tm_sec = rtc.second;// TODO : 测试转换函数，测试时间戳

    rtc_local_to_utc(&t_local, &t_utc);
    
    nResult = (uint32_t)mktime(&t_utc);
    
    return nResult;
}

/** ****************************************************************************
* @remarks       static void count_to_rtc( uint32_t TimerCount, en_sys_rtc_type_t time_mode, rtc_struct *p_rtc )
* @brief         将时间戳转变为RTC时间
* @param[in]     TimerCount 时间戳
* @param[in]     time_mode RTC数据模式
* @param[out]    *p_rtc 输出时钟地址
* @return        无
* @attention     无
*******************************************************************************/
static void count_to_rtc( uint32_t TimerCount, en_sys_rtc_type_t time_mode, rtc_struct *p_rtc )
{
    struct tm *t = localtime((const time_t *)&TimerCount);
    
    p_rtc->year = t->tm_year + 1900;
    p_rtc->month = t->tm_mon + 1;
    p_rtc->day = t->tm_mday;
    p_rtc->hour = t->tm_hour;
    p_rtc->minute = t->tm_min;
    p_rtc->second = t->tm_sec;

    rtc_utc_to_local(p_rtc, 8);
    
    if( time_mode == RTC_TIM_CAL_BCD )
    {
        p_rtc->year -= 2000;
        rtc_int_to_bcd( p_rtc );
    }
}

/** ****************************************************************************
* @remarks       static uint8_t rtc_get_io(en_sys_rtc_type_t str_type, rtc_struct *p_time)
* @brief         获取系统时间
* @param[in]     str_type 时间模式
* @param[out]    *p_rtc 输出时钟地址
* @return        1 获取时间成功，0 获取时间失败
* @attention     无
*******************************************************************************/
static uint8_t rtc_get_io(en_sys_rtc_type_t str_type, rtc_struct *p_time)
{
    hal_rtc_time_t sys_time;
    rtc_struct rtc_time;
    if(( str_type != RTC_TIM_CAL_INT )&&( str_type != RTC_TIM_CAL_BCD ))
    {
        return 0;
    }
    
    gaoz_get_rtc(&sys_time);
    rtc_time.year = sys_time.year;
    rtc_time.month = sys_time.month;
    rtc_time.day = sys_time.day;
    rtc_time.hour = sys_time.hour;
    rtc_time.minute = sys_time.min;
    rtc_time.second = sys_time.sec;
    
    if( str_type == RTC_TIM_CAL_BCD )
    {
        rtc_int_to_bcd(&rtc_time);
        rtc_time.year += 0x2000;
    }
    else if( str_type == RTC_TIM_CAL_INT )
    {
        rtc_time.year += 2000;
    }
    memcpy(p_time, &rtc_time, sizeof(rtc_struct));
    return 1;
}

/** ****************************************************************************
* @remarks       static uint8_t rtc_set_io(en_sys_rtc_type_t str_type, rtc_struct *p_time)
* @brief         设置系统时间
* @param[in]     str_type 时间模式
* @param[in]     *p_time 设置时间的数据地址
* @param[out]    无
* @return        1 获取时间成功，0 获取时间失败
* @attention     无
*******************************************************************************/
static uint8_t rtc_set_io(en_sys_rtc_type_t str_type, rtc_struct *p_time)
{
    hal_rtc_time_t sys_time;
    rtc_struct rtc_time;
    uint8_t ret = 0;
    
    if(( str_type != RTC_TIM_CAL_INT )&&( str_type != RTC_TIM_CAL_BCD ))
    {
        return 0;
    }
    memcpy(&rtc_time, p_time, sizeof(rtc_struct));
    if( str_type == RTC_TIM_CAL_BCD )
    {
        rtc_bcd_to_int(&rtc_time);
        if( sys_time.year >= 2000 )
        {
            sys_time.year -= 2000;
        }
    }
    else if( str_type == RTC_TIM_CAL_INT )
    {
        if( sys_time.year >= 2000 )
        {
            sys_time.year -= 2000;
        }
    }
    sys_time.year = rtc_time.year;
    sys_time.month = rtc_time.month;
    sys_time.day = rtc_time.day;
    sys_time.hour = rtc_time.hour;
    sys_time.min = rtc_time.minute;
    sys_time.sec = rtc_time.second;
    if( gaoz_set_rtc(&sys_time) == 0 )
    {
        ret = 1;
    }
    return ret;
}

/** ****************************************************************************
* @remarks       static uint32_t rtc_get( en_sys_rtc_type_t time_mode, void* pv )
* @brief         获取系统时间
* @param[in]     time_mode 时间模式
* @param[in]     pv 输入RTC数据的指针 不固定数据格式
* @param[out]    无
* @return        获取RTC数据长度
* @attention     无
*******************************************************************************/
static uint32_t rtc_get( en_sys_rtc_type_t time_mode, void* pv )
{
    rtc_struct rtc;
    char* p = NULL;
    switch( time_mode )
    {
        case RTC_TIM_TIMESTAMP:
            rtc_get_io( RTC_TIM_CAL_INT, &rtc );
            *(( uint32_t * )pv ) = rtc_to_count( RTC_TIM_CAL_INT, &rtc );
            return 4;

        case RTC_TIM_CAL_INT:
            rtc_get_io( RTC_TIM_CAL_INT, ( rtc_struct* )pv );
            return sizeof( rtc_struct );
        
        case RTC_TIM_CAL_BCD:
            rtc_get_io( RTC_TIM_CAL_BCD, ( rtc_struct* )pv );
            return sizeof( rtc_struct );
        
        case RTC_TIM_STRING:
            rtc_get_io( RTC_TIM_CAL_BCD, &rtc );
            p = ( char* )pv;
            
            p[0] = (( rtc.year >> 12 ) & 0x0F ) + '0';
            p[1] = (( rtc.year >> 8 ) & 0x0F ) + '0';
            p[2] = (( rtc.year >> 4 ) & 0x0F ) + '0';
            p[3] = ( rtc.year & 0x0F ) + '0';
            p[4] = '-';
            p[5] = (( rtc.month >> 4 ) & 0x0F ) + '0';
            p[6] = ( rtc.month & 0x0F ) + '0';
            p[7] = '-';
            p[8] = (( rtc.day >> 4 ) & 0x0F ) + '0';
            p[9] = ( rtc.day & 0x0F ) + '0';
            p[10] = ' ';

            p[11] = (( rtc.hour >> 4 ) & 0x0F ) + '0';
            p[12] = ( rtc.hour & 0x0F ) + '0';
            p[13] = ':';
            p[14] = (( rtc.minute >> 4 ) & 0x0F ) + '0';
            p[15] = ( rtc.minute & 0x0F ) + '0';
            p[16] = ':';
            p[17] = (( rtc.second >> 4 ) & 0x0F ) + '0';
            p[18] = ( rtc.second & 0x0F ) + '0';
            return 19;

        case RTC_TIM_ASCII:
            rtc_get_io( RTC_TIM_CAL_BCD, &rtc );
            p = ( char* )pv;
            p[0] = (( rtc.year >> 12 ) & 0x0F ) + '0';
            p[1] = (( rtc.year >> 8 ) & 0x0F ) + '0';
            p[2] = (( rtc.year >> 4 ) & 0x0F ) + '0';
            p[3] = ( rtc.year & 0x0F ) + '0';
            p[4] = (( rtc.month >> 4 ) & 0x0F ) + '0';
            p[5] = ( rtc.month & 0x0F ) + '0';
            p[6] = (( rtc.day >> 4 ) & 0x0F ) + '0';
            p[7] = ( rtc.day & 0x0F ) + '0';

            p[8] = (( rtc.hour >> 4 ) & 0x0F ) + '0';
            p[9] = ( rtc.hour & 0x0F ) + '0';
            p[10] = (( rtc.minute >> 4 ) & 0x0F ) + '0';
            p[11] = ( rtc.minute & 0x0F ) + '0';
            p[12] = (( rtc.second >> 4 ) & 0x0F ) + '0';
            p[13] = ( rtc.second & 0x0F ) + '0';
            return 14;

        default:
            rtc_get_io( RTC_TIM_CAL_INT, ( rtc_struct* )pv );
            return sizeof( rtc_struct );
    }
}

/** ****************************************************************************
* @remarks       static uint32_t rtc_set( en_sys_rtc_type_t time_mode, void* pv )
* @brief         RTC设置时间
* @param[in]     time_mode 时间模式
* @param[in]     pv 输入RTC数据的指针 不固定数据格式
* @param[out]    无
* @return        1 获取时间成功，0 获取时间失败
* @attention     无
*******************************************************************************/
static uint32_t rtc_set( en_sys_rtc_type_t time_mode, void* pv )
{
    rtc_struct rtc;
    uint32_t ret = 0;
    if( time_mode == RTC_TIM_TIMESTAMP )
    {
        count_to_rtc( *( uint32_t * )( pv ), RTC_TIM_CAL_INT, &rtc );
        ret = rtc_set_io( RTC_TIM_CAL_INT, &rtc );
    }
    else if( time_mode == RTC_TIM_CAL_INT )
    {
        ret = rtc_set_io( RTC_TIM_CAL_INT, ( rtc_struct* )pv );
    }
    else if( time_mode == RTC_TIM_CAL_BCD )
    {
        ret =rtc_set_io( RTC_TIM_CAL_BCD, ( rtc_struct* )pv );
    }
    else 
    {
        return ret;
    }
    return ret;
}

/** ****************************************************************************
* @remarks       uint32_t rtc_sys_get( en_sys_rtc_type_t time_mode, void* pv )
* @brief         获取系统时间
* @param[in]     time_mode 时间模式
* @param[out]    * pv RTC数据的指针 不固定数据格式
* @return        获取系统时钟的长度
* @attention     函数为半可重入函数,
*******************************************************************************/
uint32_t rtc_sys_get( en_sys_rtc_type_t time_mode, void* pv )
{
    uint32_t  osR = 0;
    uint32_t  res = 0;
    if(pv == NULL)
    {
        return res;
    }
    if( rtc_info_mutex != 0 )
    {
        gaoz_mutex_lock(rtc_info_mutex);
        osR = 1;
    }
    res = rtc_get(time_mode , pv );
    if(osR != 0)
    {
        gaoz_mutex_unlock(rtc_info_mutex);
    }
    return (res);
}
    
/** ****************************************************************************
* @remarks       uint32_t rtc_sys_set( en_sys_rtc_type_t time_mode, void* pv )
* @brief         设置系统时间
* @param[in]     time_mode 时间模式
* @param[in]     * pv RTC数据的指针 不固定数据格式
* @return        pdPASS 设置时间成功，pdFAIL 设置时间失败
* @attention     函数为半可重入函数
*******************************************************************************/
uint32_t rtc_sys_set( en_sys_rtc_type_t time_mode, void* pv )
{
    uint32_t  osR = 0;
    uint32_t  res = pdFAIL; 
    if(pv == NULL)
    {
        return res;
    }
    if( rtc_info_mutex != 0 )
    {
        gaoz_mutex_lock(rtc_info_mutex);
        osR = 1;
    }
    if( rtc_set(time_mode , pv ) == 1 )
    {
        res = pdPASS;
    }
    if(osR != 0)
    {
        gaoz_mutex_unlock(rtc_info_mutex);
    }
    return (res);
}


